#include "ST7789.h"
 
void LCD_GPIO_init(void)
{
#if LCD_USE_HARDWARE_SPI == 1
 
    gpio_init_type gpio_init_struct;
 
    // =========================> 初始化 SPI数据总线 GPIO <=========================
    crm_periph_clock_enable(LCD_SPIx_GPIO_CRM, TRUE);
 
    gpio_default_para_init(&gpio_init_struct);
 
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    // SCLK
    gpio_init_struct.gpio_pull = GPIO_PULL_DOWN;
    gpio_init_struct.gpio_pins = LCD_SPIx_SCLK_GPIO_PIN;
    gpio_init(LCD_SPIx_GPIO, &gpio_init_struct);
    gpio_pin_mux_config(LCD_SPIx_GPIO, LCD_SPIx_SCLK_SOURCE, LCD_SPIx_MUX_SEL);
 
    // MOSI
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init_struct.gpio_pins = LCD_SPIx_MOSI_GPIO_PIN;
    ;
    gpio_init(LCD_SPIx_GPIO, &gpio_init_struct);
    gpio_pin_mux_config(LCD_SPIx_GPIO, LCD_SPIx_MOSI_SOURCE, LCD_SPIx_MUX_SEL);
 
#if LCD_SHARE_SPI_WITH_TOUCH_IC == 1
    // LCD和触摸IC共用SPI，CS由软件管理，此处不作配置
    // 由于要接收触摸IC的数据，需要配置MISO，注意XPT2046的数据输出引脚外部不能上拉或下拉，只能配置成浮空输入
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
    gpio_init_struct.gpio_pins = LCD_SPIx_MISO_GPIO_PIN;
    gpio_init(LCD_SPIx_GPIO, &gpio_init_struct);
    gpio_pin_mux_config(LCD_SPIx_GPIO, LCD_SPIx_MISO_SOURCE, LCD_SPIx_MUX_SEL);
#else
    // LCD单独使用SPI，无需配置MISO，并且为硬件管理CS，
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init_struct.gpio_pins = LCD_SPIx_CS_GPIO_PIN;
    gpio_init(LCD_SPIx_GPIO, &gpio_init_struct);
    gpio_pin_mux_config(LCD_SPIx_GPIO, LCD_SPIx_CS_SOURCE, LCD_SPIx_MUX_SEL);
#endif
 
    // =========================> 初始化其余信号线的GPIO <=========================
    crm_periph_clock_enable(LCD_GPIOPORT_CRM, TRUE);
 
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
 
#if LCD_SHARE_SPI_WITH_TOUCH_IC == 1
    // LCD和触摸IC共用SPI，CS由MCU直接控制GPIO实现
    gpio_init_struct.gpio_pins = LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK | LCD_PINS_CS;
    gpio_init(LCD_GPIOPORT, &gpio_init_struct);
    gpio_bits_set(LCD_GPIOPORT, LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK | LCD_PINS_CS);
#else
    // LCD单独使用SPI，CS已经由硬件实现，此处不作配置
    #error "Hardware CS is is a piece of shit, DO NOT try!"
    gpio_init_struct.gpio_pins = LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK;
    gpio_init(LCD_GPIOPORT, &gpio_init_struct);
    gpio_bits_set(LCD_GPIOPORT, LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK);
#endif
 
#else
    // 使用软件SPI 且必定共用一个SPI
    crm_periph_clock_enable(LCD_GPIOPORT_CRM, TRUE);
 
    gpio_init_type gpio_init_struct;
    gpio_default_para_init(&gpio_init_struct);
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init_struct.gpio_pins = LCD_PINS_SCLK | LCD_PINS_MOSI | LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK | LCD_PINS_CS;
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
 
    gpio_init(LCD_GPIOPORT, &gpio_init_struct);
 
    gpio_bits_set(LCD_GPIOPORT, LCD_PINS_SCLK | LCD_PINS_MOSI | LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK | LCD_PINS_CS);
#endif
}
 
#if LCD_USE_HARDWARE_SPI == 1 && LCD_HARDWARE_SPI_USE_DMA == 0
 
/**
 
  * @brief  spi configuration.
  * @param  none
  * @retval none
  */
void LCD_SPIx_init(void)
{
    spi_init_type spi_init_struct;
 
    spi_i2s_reset(LCD_SPIx);
    crm_periph_clock_enable(LCD_SPIx_CRM, TRUE);
    spi_default_para_init(&spi_init_struct);
    spi_init_struct.master_slave_mode = SPI_MODE_MASTER;        // 设置SPI工作模式：主机模式
    spi_init_struct.mclk_freq_division = LCD_SPI_SPEED;        // 288M / 8 = 36M
    spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB; // 数据传输高位先行
    spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;             // 设置SPI数据大小：8位帧结构
    spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_HIGH;   // 串行同步时钟空闲时SCLK位高电平
#if LCD_SHARE_SPI_WITH_TOUCH_IC == 1
    spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;        // 串行同步时钟空第二个时钟沿捕获
    spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX; // 要接收触模信息所以是全双工
    spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;     // 片选信号由软件管理
#else
    spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;        // 串行同步时钟空第二个时钟沿捕获
    spi_init_struct.transmission_mode = SPI_TRANSMIT_HALF_DUPLEX_TX; // LCD只接收信息所以是单工
    spi_init_struct.cs_mode_selection = SPI_CS_HARDWARE_MODE;        // 片选信号由硬件管理
    #error "Hardware CS is is a piece of shit, DO NOT try!"
    spi_hardware_cs_output_enable(LCD_SPIx, TRUE);
#endif
    spi_init(LCD_SPIx, &spi_init_struct);
    spi_enable(LCD_SPIx, TRUE);
}
 
 
 
#elif LCD_HARDWARE_SPI_USE_DMA == 1
 
// uint16_t LCD_SPIx_TX_buffer[LCD_SPI_TX_BUFFER_SIZE];
// #if (LCD_SHARE_SPI_WITH_TOUCH_IC == 1)
// uint16_t LCD_SPIx_RX_buffer[LCD_SPI_RX_BUFFER_SIZE];
// #endif
 
void LCD_SPIx_init(void)
{
 
    dma_init_type dma_init_struct;
    spi_init_type spi_init_struct;
    
    /************** DMA 配置 *****************/
 
    /* LCD_SPI_TX_DMAx_CHy configuration */
    // 时钟配置
    crm_periph_clock_enable(LCD_SPI_TX_DMAx_CRM_CLOCK, TRUE);
    dma_reset(LCD_SPI_TX_DMAx_CHy);
    // DMA MUX 通道配置
    dmamux_enable(LCD_SPI_TX_DMAx, TRUE);
    dmamux_init(LCD_SPI_TX_DMAxMUX_CHy, LCD_SPI_TX_DMAMUX_REQ_ID);
    // DMA 通道配置
    dma_default_para_init(&dma_init_struct);
    dma_init_struct.buffer_size = LCD_SPI_TX_BUFFER_SIZE;
    dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
    // dma_init_struct.memory_base_addr = (uint32_t)LCD_SPIx_TX_buffer;
    dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;
    dma_init_struct.memory_inc_enable = TRUE;
    dma_init_struct.peripheral_base_addr = (uint32_t)&(LCD_SPIx->dt);
    // dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE; // 如果 MWIDTH 是16bit，但 PWIDTH 是8bit，方向是M2P，那么DMA只会搬运存MEM中每个半字的低8位，也即低地址处的字节。
    dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;
    dma_init_struct.peripheral_inc_enable = FALSE;
    dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
    dma_init_struct.loop_mode_enable = FALSE; //是否循环模式
    dma_init(LCD_SPI_TX_DMAx_CHy, &dma_init_struct);
 
    #if (LCD_SHARE_SPI_WITH_TOUCH_IC == 1)
    /* LCD_SPI_TX_DMAx_CHy configuration */
    // DMA MUX 通道配置
    dmamux_init(LCD_SPI_RX_DMAxMUX_CHy, LCD_SPI_RX_DMAMUX_REQ_ID);
    // DMA 通道配置
    dma_default_para_init(&dma_init_struct);
    dma_init_struct.buffer_size = LCD_SPI_RX_BUFFER_SIZE;
    dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
    // dma_init_struct.memory_base_addr = (uint32_t)LCD_SPIx_RX_buffer;
    dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
    dma_init_struct.memory_inc_enable = TRUE;
    dma_init_struct.peripheral_base_addr = (uint32_t)&(LCD_SPIx->dt);
    dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
    dma_init_struct.peripheral_inc_enable = FALSE;
    dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
    dma_init_struct.loop_mode_enable = FALSE; //是否循环模式
    dma_init(LCD_SPI_RX_DMAx_CHy, &dma_init_struct);
    #else
    // 不需要 RX buffer 和 RX DMA
    #endif
 
    /************** SPI 配置 *****************/
 
    crm_periph_clock_enable(LCD_SPIx_CRM, TRUE);
    spi_default_para_init(&spi_init_struct);
    #if (LCD_SHARE_SPI_WITH_TOUCH_IC == 1)
    spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;
    #else
    spi_init_struct.transmission_mode = SPI_TRANSMIT_HALF_DUPLEX_TX;
    #endif
    spi_init_struct.master_slave_mode = SPI_MODE_MASTER;
    spi_init_struct.mclk_freq_division = LCD_SPI_SPEED;
    spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;
    spi_init_struct.frame_bit_num = SPI_FRAME_16BIT;
    spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_HIGH;
    spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;
    spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;
 
    spi_init(LCD_SPIx, &spi_init_struct);
    spi_i2s_dma_transmitter_enable(LCD_SPIx, TRUE);
    // spi_i2s_dma_receiver_enable(LCD_SPIx,TRUE);
    spi_enable(LCD_SPIx, TRUE);
 
    #if (LCD_HARDWARE_SPI_TX_DMA_USE_INT == 1)
    //中断配置
    nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
 
    /* enable transfer full data intterrupt 当数据全部发送完毕后产生中断*/
    dma_interrupt_enable(LCD_SPI_TX_DMAx_CHy, DMA_FDT_INT, TRUE);
    /* LCD_SPI_TX_DMAx_CHy interrupt nvic init */
    nvic_irq_enable(LCD_SPI_TX_DMAx_CHy_IRQn, 5, 0);
    /* config flexible dma for LCD_SPI_TX_DMAxMUX_CHy 配置弹性映射 */
    dma_flexible_config(LCD_SPI_TX_DMAx, LCD_SPI_TX_DMAxMUX_CHy, LCD_SPI_TX_DMAMUX_REQ_ID);
 
    #if (LCD_SHARE_SPI_WITH_TOUCH_IC == 1)
    /* enable transfer full data intterrupt 当数据全部发送完毕后产生中断*/
    dma_interrupt_enable(LCD_SPI_RX_DMAx_CHy, DMA_FDT_INT, TRUE);
    /* LCD_SPI_RX_DMAx_CHy interrupt nvic init */
    nvic_irq_enable(LCD_SPI_RX_DMAx_CHy_IRQn, 3, 0);
    /* config flexible dma for LCD_SPI_RX_DMAxMUX_CHy 配置弹性映射 */
    dma_flexible_config(LCD_SPI_TX_DMAx, LCD_SPI_RX_DMAxMUX_CHy, LCD_SPI_RX_DMAMUX_REQ_ID);
    #endif
    #endif
    
}
 
/**
  * @brief  data transfer to LCD_SPIx using DMA
  * @param  buf : buffer address
  * @param  size : this uint32_t value should be less than 65535. Or otherwise size%0xFFFF is what finally takes effect.
  * @retval none
  */
void LCD_SPI_data_send_use_DMA(const void *buf, uint32_t size)
{
    #if (LCD_HARDWARE_SPI_TX_DMA_USE_INT == 1)
    // 在使用 SPI TX DMA 中断的情况下，如果此时DMA还在搬运，那么应该等DMA中断服务程序结束后再向DMA发送新的buf和size，否则DMA传输中断，屏幕收到数据不完整
    if (LCD_SPI_TX_DMAx_CHy->ctrl_bit.chen == 1)
    {
        __WFI();
    }
    #endif
    // 确保 LCD_SPIx 为16bit传输模式 且 频率为初始设定频率
    if(LCD_SPIx->ctrl1_bit.fbn == SPI_FRAME_8BIT || LCD_SPIx->ctrl1_bit.mdiv_l != LCD_SPI_SPEED)
    {
        // 需要把SPI改为16bit模式 或 把频率改为设定频率
 
        // DMA搬运完成，但此时SPI还在发送，等待SPI通信忙结束
        while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零，说明还有数据在传输
        {
            // 等待主机数据发送完毕
        }
        while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Rx dt buf满 清零，说明还有数据未发送
        {
            // 等待主机数据接收完毕
        }
        spi_i2s_data_receive(LCD_SPIx);
        // SPI改为16bit模式
        spi_frame_bit_num_set(LCD_SPIx, SPI_FRAME_16BIT);
        // SPI时钟分频改为初始设定值
        LCD_SPIx->ctrl1_bit.mdiv_l = LCD_SPI_SPEED;
    }
 
    // 确保对应通道的 CHEN 位为 0，否则无法写入
    dma_channel_enable(LCD_SPI_TX_DMAx_CHy, FALSE);
    LCD_SPI_TX_DMAx_CHy->maddr = (uint32_t)buf;
    LCD_SPI_TX_DMAx_CHy->dtcnt_bit.cnt = size; // 注意 dtcnt 是一个32位寄存器，但只有低16位是可用的
    // 开启DMA搬运前拉低CS
    LCD_CS_CLR();
    // DMA开始搬运，随即SPI开始传输
    dma_channel_enable(LCD_SPI_TX_DMAx_CHy, TRUE);
    // =============================> 接收通道的DMA配置 <=============================
    #if (LCD_SHARE_SPI_WITH_TOUCH_IC == 1)
    dma_channel_enable(LCD_SPI_RX_DMAx_CHy, FALSE);
    // 确保对应通道的 CHEN 位为 0，否则无法写入
    // LCD_SPI_RX_DMAx_CHy->maddr = (uint32_t)LCD_SPIx_RX_buffer;
    LCD_SPI_RX_DMAx_CHy->dtcnt_bit.cnt = size; // 注意 dtcnt 是一个32位寄存器，但只有低16位是可用的
    dma_channel_enable(LCD_SPI_RX_DMAx_CHy, TRUE); // （在轮询结束或在中断服务程序的最后会关掉）
    #endif
 
    #if LCD_HARDWARE_SPI_TX_DMA_USE_INT == 1
    // 将在中断服务程序中完成 标志位清除、等待SPI发送结束并读一次数据（如果是全双工）、关闭DMA通道、拉高CS 的工作
    #else
    /*** 若不开DMA完成中断，则轮询DMAx_FDTy标志位 ***/
    // while(spi_i2s_flag_get(LCD_SPIx,SPI_I2S_BF_FLAG) == SET){};
    // while(dma_flag_get(DMA2_FDT1_FLAG) == RESET)
    // {
    // };
    // dma_flag_clear(DMA2_FDT1_FLAG);
    // LCD_CS_SET();
    // // spi_i2s_dma_transmitter_enable(LCD_SPIx, FALSE);
    // dma_channel_enable(LCD_SPI_TX_DMAx_CHy, FALSE);
    // spi_i2s_data_receive(LCD_SPIx);
    // // lv_disp_flush_complete();
 
    #endif
}
#if LCD_HARDWARE_SPI_TX_DMA_USE_INT == 1
// 将在中断服务程序中完成 标志位清除、等待SPI发送结束并读一次数据（如果是全双工）、关闭DMA通道、拉高CS 的工作
 
void LCD_SPI_TX_DMAx_CHy_IRQHandler(void)
{
    /*DMA发送完成中断*/
    if (dma_flag_get(LCD_SPI_TX_DMAx_FDTy_FLAG) == SET)
    {
        dma_channel_enable(LCD_SPI_TX_DMAx_CHy, FALSE);
 
        // DMA搬运完成，但此时SPI还在发送
        while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET || spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == SET || spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET)// Tx dt buf非空 或者 Rx dt buf满 或者 通信忙
        {
            spi_i2s_data_receive(LCD_SPIx);
        }
        while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_ROERR_FLAG) == SET || spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET)// Tx dt buf非空 或者 Rx dt buf满 或者 通信忙
        {
            spi_i2s_data_receive(LCD_SPIx);
        }
        spi_i2s_data_receive(LCD_SPIx); // ！！！血泪教训！！全双工下DMA发完SPI后一定要读一次！！！
        LCD_CS_SET();
        // // DMA改回默认设置：MEM地址递增 （刷纯色的时候会改成FALSE）
        // LCD_SPI_TX_DMAx_CHy->ctrl_bit.mincm = TRUE;
        dma_flag_clear(LCD_SPI_TX_DMAx_FDTy_FLAG);
    }
}
 
#endif
 
void LCD_SPI_RX_DMAx_CHy_IRQHandler(void)
{
    /*DMA发送完成中断*/
    if (dma_flag_get(LCD_SPI_RX_DMAx_FDTy_FLAG) == SET)
    {
        dma_flag_clear(LCD_SPI_RX_DMAx_FDTy_FLAG);
        // LCD_SPI_RX_DMAx_CHy 将在发送数据的时候被再次使能
        dma_channel_enable(LCD_SPI_RX_DMAx_CHy, FALSE);
    }
}
#endif
 
/******************************************************************************
     函数说明：LCD串行数据写入函数
    入口数据：data  要写入的串行数据
    返回值：  无
******************************************************************************/
static void LCD_SPI_write_bus(uint16_t data)
{
#if (LCD_USE_HARDWARE_SPI == 1)
    // #if (LCD_HARDWARE_SPI_USE_DMA == 1)
    // LCD_SPI_data_send_use_DMA(&data, 1);
    // #else
    LCD_CS_CLR();
    while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零，说明还有数据在传输
    {
        // 等待主机数据发送完毕
    }
    spi_i2s_data_transmit(LCD_SPIx, data);
    while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Rx dt buf满 清零，说明还有数据未发送
    {
        // 等待主机数据接收完毕
    }
    spi_i2s_data_receive(LCD_SPIx);// 即使实际上没有用到 MISO，也需要读一次DT寄存器
    // while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零，说明还有数据在传输
    // {
    //     // 等待主机数据发送完毕
    //     // 他说他自己发完了，其实他发完个屁
    // }
    LCD_CS_SET();
    // #endif
#else
    uint8_t i;
    LCD_CS_CLR();
    for (i = 0; i < 8; i++) {
        LCD_SCLK_CLR();
        if (data & 0x80) { 
            LCD_MOSI_SET();
        } else {
            LCD_MOSI_CLR();
        }
        LCD_SCLK_SET();
        data <<= 1;
    }
    LCD_CS_SET();
#endif
}
 
/******************************************************************************
     函数说明：LCD写入数据
    入口数据：data 写入的数据
    返回值：  无
******************************************************************************/
void LCD_write_byte(uint8_t data_byte)
{
    // 确保 LCD_SPIx 为8bit传输模式
    if(LCD_SPIx->ctrl1_bit.fbn == SPI_FRAME_16BIT)
    {
        // 需要把SPI改为16bit模式
        // 等待SPI通信忙结束
        while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零，说明还有数据在传输
        {
            // 等待主机数据发送完毕
        }
        while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Rx dt buf满 清零，说明还有数据未发送
        {
            // 等待主机数据接收完毕
        }
        spi_i2s_data_receive(LCD_SPIx);
        // SPI改为8bit模式
        spi_frame_bit_num_set(LCD_SPIx, SPI_FRAME_8BIT);
    }
    LCD_SPI_write_bus(data_byte);
}
 
/******************************************************************************
     函数说明：LCD写入数据
    入口数据：data 写入的数据
    返回值：  无
******************************************************************************/
void LCD_write_half(uint16_t data_half)
{
    // 确保 LCD_SPIx 为16bit传输模式
    if(LCD_SPIx->ctrl1_bit.fbn == SPI_FRAME_8BIT)
    {
        // 需要把SPI改为16bit模式
 
        // 等待SPI通信忙结束
        while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零，说明还有数据在传输
        {
            // 等待主机数据发送完毕
        }
        while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Rx dt buf满 清零，说明还有数据未发送
        {
            // 等待主机数据接收完毕
        }
        spi_i2s_data_receive(LCD_SPIx);
        // SPI改为16bit模式
        spi_frame_bit_num_set(LCD_SPIx, SPI_FRAME_16BIT);
    }
    LCD_SPI_write_bus(data_half);
 
}
 
/******************************************************************************
     函数说明：LCD写入命令
    入口数据：data 写入的命令
    返回值：  无
******************************************************************************/
void LCD_write_cmd(uint8_t data_byte)
{
    #if (LCD_HARDWARE_SPI_TX_DMA_USE_INT == 1)
    // 在使用 SPI TX DMA 中断的情况下，如果此时DMA还在搬运，那么应该等DMA中断服务程序结束，并且SPI当前帧发送结束后再把DC拉低，否则将导致cmd数据和data数据错位，屏幕卡死
    if (LCD_SPI_TX_DMAx_CHy->ctrl_bit.chen == 1)
    {
    __WFI();
    }
    // DMA搬运完成，但此时SPI还在发送
    while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零，说明还有数据在传输
    {
        // 等待主机数据发送完毕
    }
    while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Rx dt buf满 清零，说明还有数据未发送
    {
        // 等待主机数据接收完毕
    }
    spi_i2s_data_receive(LCD_SPIx);
    #endif
    LCD_DC_CLR(); //写命令
    LCD_write_byte(data_byte);
    LCD_DC_SET(); //写数据
}
 
 
/**
  * @brief  设置区域的左上角（起始）和右下角（结束）
  * @param  x1 : 列起始坐标
  * @param  y1 : 行起始坐标
  * @param  x2 : 列结束坐标
  * @param  y2 : 行结束坐标
  * @retval none
  */
void LCD_addr_set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
    // 列地址设置
    LCD_write_cmd(0x2a); 
    LCD_write_half(x1);
    LCD_write_half(x2);
    // 行地址设置
    LCD_write_cmd(0x2b);
    LCD_write_half(y1);
    LCD_write_half(y2);
    // 准备接收数据
    LCD_write_cmd(0x2c); 
}
 
/******************************************************************************
     函数说明：LCD初始化函数
    入口数据：无
    返回值：  无
******************************************************************************/
void LCD_init(void)
{
#if CHIP_USE_ST7789 == 0 //初始化ILI9341
    {
        LCD_RES_CLR(); //复位
        delay_ms(100);
        LCD_RES_SET();
        delay_ms(100);
 
        LCD_BLK_SET(); //打开背光
        delay_ms(100);
 
        //************* Start Initial Sequence **********//
        LCD_write_cmd(0x11); // Sleep out
        delay_ms(120);       // Delay 120ms
        //************* Start Initial Sequence **********//
        LCD_write_cmd(0xCF);
        LCD_write_byte(0x00);
        LCD_write_byte(0xC1);
        LCD_write_byte(0X30);
        LCD_write_cmd(0xED);
        LCD_write_byte(0x64);
        LCD_write_byte(0x03);
        LCD_write_byte(0X12);
        LCD_write_byte(0X81);
        LCD_write_cmd(0xE8);
        LCD_write_byte(0x85);
        LCD_write_byte(0x00);
        LCD_write_byte(0x79);
        LCD_write_cmd(0xCB);
        LCD_write_byte(0x39);
        LCD_write_byte(0x2C);
        LCD_write_byte(0x00);
        LCD_write_byte(0x34);
        LCD_write_byte(0x02);
        LCD_write_cmd(0xF7);
        LCD_write_byte(0x20);
        LCD_write_cmd(0xEA);
        LCD_write_byte(0x00);
        LCD_write_byte(0x00);
        LCD_write_cmd(0xC0);  // Power control
        LCD_write_byte(0x1D); // VRH[5:0]
        LCD_write_cmd(0xC1);  // Power control
        LCD_write_byte(0x12); // SAP[2:0];BT[3:0]
        LCD_write_cmd(0xC5);  // VCM control
        LCD_write_byte(0x33);
        LCD_write_byte(0x3F);
        LCD_write_cmd(0xC7); // VCM control
        LCD_write_byte(0x92);
        LCD_write_cmd(0x3A); // Memory Access Control
        LCD_write_byte(0x55);
        LCD_write_cmd(0x36); // Memory Access Control
        if (USE_HORIZONTAL == 0)
            LCD_write_byte(0x08);
        else if (USE_HORIZONTAL == 1)
            LCD_write_byte(0xC8);
        else if (USE_HORIZONTAL == 2)
            LCD_write_byte(0x78);
        else
            LCD_write_byte(0xA8);
        LCD_write_cmd(0xB1);
        LCD_write_byte(0x00);
        LCD_write_byte(0x12);
        LCD_write_cmd(0xB6); // Display Function Control
        LCD_write_byte(0x0A);
        LCD_write_byte(0xA2);
 
        LCD_write_cmd(0x44);
        LCD_write_byte(0x02);
 
        LCD_write_cmd(0xF2); // 3Gamma Function Disable
        LCD_write_byte(0x00);
        LCD_write_cmd(0x26); // Gamma curve selected
        LCD_write_byte(0x01);
        LCD_write_cmd(0xE0); // Set Gamma
        LCD_write_byte(0x0F);
        LCD_write_byte(0x22);
        LCD_write_byte(0x1C);
        LCD_write_byte(0x1B);
        LCD_write_byte(0x08);
        LCD_write_byte(0x0F);
        LCD_write_byte(0x48);
        LCD_write_byte(0xB8);
        LCD_write_byte(0x34);
        LCD_write_byte(0x05);
        LCD_write_byte(0x0C);
        LCD_write_byte(0x09);
        LCD_write_byte(0x0F);
        LCD_write_byte(0x07);
        LCD_write_byte(0x00);
        LCD_write_cmd(0XE1); // Set Gamma
        LCD_write_byte(0x00);
        LCD_write_byte(0x23);
        LCD_write_byte(0x24);
        LCD_write_byte(0x07);
        LCD_write_byte(0x10);
        LCD_write_byte(0x07);
        LCD_write_byte(0x38);
        LCD_write_byte(0x47);
        LCD_write_byte(0x4B);
        LCD_write_byte(0x0A);
        LCD_write_byte(0x13);
        LCD_write_byte(0x06);
        LCD_write_byte(0x30);
        LCD_write_byte(0x38);
        LCD_write_byte(0x0F);
        LCD_write_cmd(0x29); // Display on
    }
#else //初始化ST7789
    {
        LCD_RES_CLR(); //复位
        delay_ms(100);
        LCD_RES_SET();
        delay_ms(100);
        LCD_BLK_SET(); //打开背光
        delay_ms(500);
        LCD_write_cmd(0x11);
        delay_ms(120); // Delay 120ms
        //************* Start Initial Sequence **********//
        //------------------------------display and color format setting--------------------------------//
 
        LCD_write_cmd(0X36); // Memory Access Control
        if (USE_HORIZONTAL == 0)
            LCD_write_byte(0x00);
        else if (USE_HORIZONTAL == 1)
            LCD_write_byte(0xC0);
        else if (USE_HORIZONTAL == 2)
            LCD_write_byte(0x70);
        else
            LCD_write_byte(0xA0);
        LCD_write_cmd(0X3A);
        LCD_write_byte(0X05);
        //--------------------------------ST7789S Frame rate setting-------------------------
 
        LCD_write_cmd(0xb2);
        LCD_write_byte(0x0c);
        LCD_write_byte(0x0c);
        LCD_write_byte(0x00);
        LCD_write_byte(0x33);
        LCD_write_byte(0x33);
        LCD_write_cmd(0xb7);
        LCD_write_byte(0x35);
        //---------------------------------ST7789S Power setting-----------------------------
 
        LCD_write_cmd(0xbb);
        LCD_write_byte(0x35);
        LCD_write_cmd(0xc0);
        LCD_write_byte(0x2c);
        LCD_write_cmd(0xc2);
        LCD_write_byte(0x01);
        LCD_write_cmd(0xc3);
        LCD_write_byte(0x13);
        LCD_write_cmd(0xc4);
        LCD_write_byte(0x20);
        LCD_write_cmd(0xc6);
        LCD_write_byte(0x0f);
        LCD_write_cmd(0xca);
        LCD_write_byte(0x0f);
        LCD_write_cmd(0xc8);
        LCD_write_byte(0x08);
        LCD_write_cmd(0x55);
        LCD_write_byte(0x90);
        LCD_write_cmd(0xd0);
        LCD_write_byte(0xa4);
        LCD_write_byte(0xa1);
        //--------------------------------ST7789S gamma setting------------------------------
        LCD_write_cmd(0xe0);
        LCD_write_byte(0xd0);
        LCD_write_byte(0x00);
        LCD_write_byte(0x06);
        LCD_write_byte(0x09);
        LCD_write_byte(0x0b);
        LCD_write_byte(0x2a);
        LCD_write_byte(0x3c);
        LCD_write_byte(0x55);
        LCD_write_byte(0x4b);
        LCD_write_byte(0x08);
        LCD_write_byte(0x16);
        LCD_write_byte(0x14);
        LCD_write_byte(0x19);
        LCD_write_byte(0x20);
        LCD_write_cmd(0xe1);
        LCD_write_byte(0xd0);
        LCD_write_byte(0x00);
        LCD_write_byte(0x06);
        LCD_write_byte(0x09);
        LCD_write_byte(0x0b);
        LCD_write_byte(0x29);
        LCD_write_byte(0x36);
        LCD_write_byte(0x54);
        LCD_write_byte(0x4b);
        LCD_write_byte(0x0d);
        LCD_write_byte(0x16);
        LCD_write_byte(0x14);
        LCD_write_byte(0x21);
        LCD_write_byte(0x20);
        LCD_write_cmd(0x29);
    }
#endif
}